package xj.graph2d.tools;

import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.util.Observer;

import xj.graph2d.Command;
import xj.graph2d.DrawAttr;
import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeConstants;
import xj.graph2d.DrawShapeFactory;
import xj.graph2d.GraphDocument;
import xj.graph2d.GraphNodeBase;
import xj.graph2d.GraphEdgeBase;
import xj.graph2d.GraphView;
import xj.graph2d.OpenShape;
import xj.graph2d.command.AddEdgeCommand;
import xj.graph2d.command.AddShapeCommand;
import xj.graph2d.graph.GraphNode;
import xj.graph2d.shapes.OrthogonalLineShape;
import xj.graph2d.shapes.PolyCurveSelfLoop;
import xj.graph2d.shapes.PolyCurveShape;
import xj.graph2d.shapes.PolyShape;
import xj.graph2d.shapes.GraphEdgeShape;
import xj.util.gui.Point2DFloat;

public class PolyShapeDrawTool extends SimpleDrawTool {

  public PolyShapeDrawTool(PolyShape shape) {
    this(shape, DrawShapeConstants.USE_SYSTEM_DRAW_ATTR, null);
  }

  public PolyShapeDrawTool(PolyShape shape, int cloneDrawAttrMode) {
    this(shape, cloneDrawAttrMode, null);
  }

  public PolyShapeDrawTool(PolyShape shape, int cloneDrawAttrMode,
			   String shapeKey) {
    super(cloneDrawAttrMode);
    this.shape = shape;
    this.shapeKey = shapeKey;
  }

  @Override
  public void mouseLButtonPressed(Observer observer, Point p, int modifiers) {
    if (debug) {
      System.out.println("PolyShapeDrawTool.mouseLButtonPressed" + 
			 " shape" + (shape == null ? "==" : "!=") + "null" + 
			 " newShape" + (newShape == null ? "==" : "!=") + "null");
    }
    start(observer);
    if (shape != null && view != null) {
      Graphics2D g2 = canvas.getGraphics2D();
      canvas.setTransform(g2);

      //g2.setXORMode(view.getBackgroundColor());

      prev = view.physicalToVirtual(p);
      float x = (float) prev.getX();
      float y = (float) prev.getY();

      if (newShape != null) {
	// continue making a polyshape
	vertexCount++;
	newShape.drawShape(g2);
	newShape.addPoint();
	adjustNewShape();
      } else {
	// making a new polyshape
	vertexCount = 0;
	DrawShape selected = doc.getSelectedShape();
	doc.setSelectedShape(null);
	canvas.repaintShape(selected);
	canvas.setEditingMode(true);
	start = new Point2DFloat(x, y);
	newShape = (PolyShape) DrawShapeFactory.cloneDrawShape(shape, view,
							       cloneDrawAttrMode);
	newShape.setStart(x, y);
	newShape.addPoint();
	adjustNewShape();

	canvas.clearRepaintShapes();
	canvas.addRepaintShape(newShape);
      }
      canvas.repaint();
      g2.dispose();
    }
  }

  @Override
  public void popupTriggered(Observer observer, Point p, int modifiers) {
    if (newShape != null && view != null) {
      mouseRButtonPressed(observer, p, modifiers);
    }
  }

  @Override
  public void mouseRButtonPressed(Observer observer, Point p, int modifiers) {
    if (debug) {
      System.out.println("PolyShapeDrawTool.mouseRButtonPressed" + 
			 " newShape" + (newShape == null ? "==" : "!=") + "null");
    }

    if (newShape != null && view != null) {
      canvas.setEditingMode(false);
      Point2D cur = view.physicalToVirtual(p);
      float x0 = (float) start.getX();
      float y0 = (float) start.getY();
      float x1 = (float) cur.getX();
      float y1 = (float) cur.getY();

      newShape.calculateBounds();

      if (newShape.getBounds().getWidth() >= 1 && 
	  newShape.getBounds().getHeight() >= 1) {
	Command cmd = null;
	if (newShape instanceof OpenShape && 
	    (cloneDrawAttrMode == USE_SYSTEM_DRAW_ATTR || 
	     cloneDrawAttrMode == CLONE_SYSTEM_DRAW_ATTR)) {
	  ((OpenShape) newShape).addArrows(view.getHeadArrowAttr(), view.getTailArrowAttr());
	}

	if (graphMode && 
	    shape instanceof GraphEdgeBase && 
	    doc instanceof GraphDocument && 
	    view instanceof GraphView) {
	  GraphEdgeBase newEdge = (GraphEdgeBase) newShape;
	  GraphDocument graph = (GraphDocument) doc;
	  GraphView gview = (GraphView) view;
	  GraphNodeBase src = graph.searchNode(start);
	  GraphNodeBase des = graph.searchNode(cur);
	  if (src != null) {
	    graph.connectSource(newEdge, src);
	  }
	  if (des != null) {
	    graph.connectDestination(newEdge, des);
	  }
	  newEdge.adjustEnds();
	  cmd = new AddEdgeCommand(gview, graph, newEdge);
	  doc.executeCommand(cmd);
	}

	newShape.calculateBounds();

	if (cmd == null) {
	  cmd = new AddShapeCommand(view, doc, newShape);
	  doc.executeCommand(cmd);
	}

	if (switchTool != null) {
	  switchTool();
	  switchTool.mouseLButtonPressed(observer, p, 0);
	  switchTool.mouseLButtonReleased(observer, p, 0);
	}
      }

      newShape = null;
      canvas.clearRepaintShapes();
      canvas.repaint();
    }

  }

  @Override
  public void mouseDragged(Observer observer, Point p, int modifiers) {

    if (debug) {
      System.out.println("PolyShapeDrawTool.mouseDragged" + " newShape" + 
			 (newShape == null ? "==" : "!=") + "null");
    }

    if (view != null) {

      displayLocation(p);

      if (newShape != null) {
	if (shapeKey != null) {
	  view.displayHelpMessage(shapeKey + MSGKEY_2);
	}

	Graphics2D g2 = canvas.getGraphics2D();
	DrawAttr attr = view.getDrawAttr();
	if (attr != null) {
	  g2.setColor(attr.getPenColor());
	}
	Point2D cur = view.physicalToVirtual(p);
	canvas.setTransform(g2);
	newShape.setLast((float) cur.getX(), (float) cur.getY());
	adjustNewShape();
	canvas.repaint();
	prev = cur;
	g2.dispose();
      } else {
	if (shapeKey != null) {
	  view.displayHelpMessage(shapeKey + MSGKEY_1);
	}
      }
    }
  }

  @Override
  public void mouseMoved(Observer observer, Point p, int modifiers) {
    if (debug) {
      System.out.println("PolyShapeDrawTool.mouseMoved" + " newShape" + 
			 (newShape == null ? "==" : "!=") + "null");
    }

    mouseDragged(observer, p, modifiers);
  }

  @Override
  public void mouseEntered(Observer observer, Point p, int modifiers) {
    start(observer);
  }

  @Override
  public void mouseExited(Observer observer, Point p, int modifiers) {

  }

  private void adjustNewShape() {
    PolyShape s = newShape;
    if (newShape instanceof GraphEdgeShape) { 
      GraphEdgeShape edge = (GraphEdgeShape) newShape;
      GraphEdgeBase es = edge.getEdgeShape();
      if (es instanceof PolyShape) { 
	s = (PolyShape) es; 
	if (s instanceof PolyCurveSelfLoop) {
	  edge.adjustSelfLoop();
	} else if (s instanceof PolyCurveShape) {
	  edge.adjustControlPoints();
	} else if (s instanceof OrthogonalLineShape) {
	  if (vertexCount < 1) {
	    edge.adjustOrientation();
	  }
	  edge.normalizePoints();
	}
      }
    } else {
      if (s instanceof PolyCurveSelfLoop) {
	PolyCurveSelfLoop loop = (PolyCurveSelfLoop) s;
	loop.adjustSelfLoop();
      } else if (s instanceof PolyCurveShape) {
	PolyCurveShape curve = (PolyCurveShape) s;
	curve.adjustControlPoints();
      } else if (s instanceof OrthogonalLineShape) {
	OrthogonalLineShape oline = (OrthogonalLineShape) s;
	if (vertexCount < 1) {
	  oline.adjustOrientation();
	}
	oline.normalizePoints();
      }
    }
  }

  protected PolyShape shape;

  protected PolyShape newShape;

  protected Point2D start;

  protected String shapeKey;

  protected int vertexCount = 0;

  protected static final String MSGKEY_1 = "Tool.message.1";

  protected static final String MSGKEY_2 = "Tool.message.2";

  protected boolean debug = false;

}
